package voip;

import java.io.*;
import java.net.*;
import java.util.Scanner;
import voip.SDPparser;

// SIP Header, SDP Header
public class Session{
	private final int BUF_SIZE = 2048;
	private final int SIP_PORT = 5060;
	
	private final String CRLF = "\r\n"; 
    
	private String stringFrom;
    private String stringTo;
    private String stringFromTag;
    private String stringToTag;
    private String stringBranch;
    private String stringCallSeq;
    private String stringCallID;
    
	public InetAddress destAddr;
	public int portNum;

    private InetAddress caller_ip;
    private String caller;
    
    private int port_num;
	private int sdpLength;
    
    private DatagramPacket rcv_packet;
    private DatagramSocket sip_socket;
    
    public String receiveMsg;
    public DatagramPacket receivePacket;
    public DatagramPacket sendPacket;
    byte data[];
    SDPparser sdpParser;	// SDPparser class
    
    public Session() 
    {
      	port_num = 0;
    	sdpLength = 0;
    	receiveMsg = "";    
    	stringFrom = "";
    	stringTo = "";
    	stringFromTag = "";
    	stringToTag  = "";
    	stringBranch = "";
    	stringCallSeq = "";
    	stringCallID = "";
		
    	data = new byte[BUF_SIZE];
		receivePacket = new DatagramPacket(data, data.length );
		sendPacket = new DatagramPacket(data, data.length);
		sdpParser = new SDPparser();
    }
    
    public void run()
    {
    	System.out.println("Session Start!");
    	startUDPSocket();
    	waitForSIPPacket();    	
    }
    
    // Create Datagram Socket(UDP)
    public void startUDPSocket()
    {
    	try
		{
			sip_socket = new DatagramSocket(SIP_PORT);
		}
		catch(SocketException socketException)
		{
			socketException.printStackTrace();
			System.exit(1);
		}
    }
    
    // Receive UDP Packets from SIP clients
    public void waitForSIPPacket()
    {
		System.out.println("Waiting for Packets");
		while(true)
		{
			try
			{
				//System.out.println("data: " + data);
				sip_socket.receive(receivePacket);
				
				destAddr = receivePacket.getAddress();
				portNum = receivePacket.getPort();
				
				System.out.println("destAddr:" + destAddr);
				System.out.println("portNum: " + portNum);
				
				// SIP Header Parsing and making response messages
				
				parsingHeader(receivePacket);				
			}
			catch(IOException ioException)
			{
				ioException.printStackTrace();
			}
		}    	
    }
    
    public void parsingHeader(DatagramPacket inpacket)
    {
    	
        rcv_packet = inpacket;
            	
    	port_num = rcv_packet.getPort();
    	caller_ip = rcv_packet.getAddress();
    	caller = caller_ip.getHostAddress();
    	
    	System.out.println("caller_ip: "+caller_ip);
    	System.out.println("port_num: " +port_num);
    	
       	receiveMsg = new String(inpacket.getData(), 0, inpacket.getLength());
		
       	// Get From, To, Call-ID string from received string
       	getSIPHeaderInfo(receiveMsg);
       	/////////////////////////////////////////////////////////////
		//	String.substing(begin_index, end_index);
		// 	begin_index is inclusive, end_index is exclusive 
		/////////////////////////////////////////////////////////////    
       	parseMethod(receiveMsg);
       	 
    }
    
    public void parseMethod(String message)
    {
    	String replyPacket = "";
    	
    	if(message.substring(0,6).contains("INVITE"))
       	{
/*
    		System.out.println("[Received INVITE]");
       		//Send Ringing message
       		replyPacket = makeResponseMsg("INVITE", caller);
           	sendPacketToCaller(replyPacket);	
*/           	
           	// Send "200 OK" message
           	System.out.println("Send 200 OK");
           	replyPacket = makeResponseMsg("200OK", caller);
           	sendPacketToCaller(replyPacket);

       	}
       	else if(message.substring(0,18).contains("SIP/2.0 100 Trying"))
       	{
       		// Send 180 Ringing message again. 
       		System.out.println("[Received Trying]");
       		replyPacket = makeResponseMsg("INVITE", caller);
           	sendPacketToCaller(replyPacket);	// Send Ringing message
           	
           	// Send "200 OK" message
           	System.out.println("Send 200 OK");
           	replyPacket = makeResponseMsg("200OK", caller);
           	sendPacketToCaller(replyPacket);
       	}
       	else if(message.substring(0,3).contains("ACK"))
       	{
       		System.out.println("[Received ACK]");
       		//replyPacket = makeResponseMsg("ACK", caller);
       		//sendPacketToCaller(replyPacket);
       	}
       	else if(message.substring(0,3).contains("BYE"))
       	{
       		System.out.println("[Received BYE]");
       		System.out.println("Sending 200OKNOSDP");
       		replyPacket = makeResponseMsg("200OKNOSDP", caller);
       		sendPacketToCaller(replyPacket);
       	}
       	else
       	{
       		System.out.println("[Other messages]");
       		replyPacket = makeResponseMsg("ERROR", caller);
       		//System.out.println(receiveMsg);       	
       	}
    }
    // Send packet to caller through UDP Connection
    public void sendPacketToCaller(String packet)
	{
		System.out.println("[Send Packet to Caller]");
		System.out.println("========================");
		byte[] sendByte = packet.getBytes();
		DatagramPacket sendPacket = new DatagramPacket(
									sendByte, sendByte.length,
									destAddr, portNum);
		
		try 
		{
			sip_socket.send(sendPacket);
		}
		catch(IOException e)
		{
			e.printStackTrace();
			System.exit(1);
		}
		System.out.println(new String(sendPacket.getData()));	// Debugging
	
	}
    // Parse "From, To, Call-ID" String
    public void getSIPHeaderInfo(String msg)
    {
    	String stringLine = "";
    	int start_index, end_index;
    	Scanner scanner = new Scanner(msg);
    	while(scanner.hasNext())
    	{
    		stringLine = scanner.nextLine();
    		
    		if(stringLine.startsWith("Via: "))
    		{
    			start_index = stringLine.indexOf("branch=");
    			stringBranch = stringLine.substring(start_index+7);
    			System.out.println("Call-ID: " + stringBranch);
    		}
    		else if(stringLine.startsWith("CSeq: "))
    		{
    			start_index = stringLine.indexOf(' ');
    			stringCallSeq = stringLine.substring(start_index+1);
    			System.out.println("CSeq: " + stringCallSeq);
    		}
    		else if(stringLine.startsWith("Call-ID: "))
    		{
    			start_index = stringLine.indexOf(' ');
    			stringCallID = stringLine.substring(start_index+1);
    			System.out.println("Call-ID: " + stringCallID);
    		}
    		else if(stringLine.startsWith("From: ") == true)
    		{
    			start_index = stringLine.indexOf('"');
    			end_index = stringLine.indexOf(';');
    			
    			stringFrom = stringLine.substring(start_index, end_index);
    			
    			start_index = stringLine.indexOf("tag=");
    			
    			stringFromTag = stringLine.substring(start_index);
    			System.out.println("From tag: " + stringFromTag);
    		}
    		else if(stringLine.startsWith("To: "))
    		{
    			start_index = stringLine.indexOf('<');
    			end_index = stringLine.indexOf(';');
    			if(end_index == -1)
    				stringTo = stringLine.substring(start_index);
    			else
        			stringFrom = stringLine.substring(start_index, end_index);
    			System.out.println("To: " + stringTo);
    		}
    		
    	}
    	scanner.close();
    	
    }
    
    public String makeResponseMsg(String method, String src_addr)
    {
    	String sipStartLine = "";
    	String sipHeader = "";
    	String sipData = "";
    	String sdpBody = "";
    	   	
    	// Parsing From, To, 
    	sipStartLine = setStartLine(method);
		
    	if(method == "INVITE")
    	{
        	sdpLength = 0;
    		// In 180 Ringing message, there is no need SDP Body
        	sipHeader = setSipHeader(method, src_addr);
    		sipData = sipStartLine + sipHeader;    		
    	}
    	else if(method == "200OK")
    	{
    		sdpBody = sdpParser.testSDPreply();
    		sdpLength = sdpBody.length();
        	sipHeader = setSipHeader(method, src_addr);
    		sipData = sipStartLine + sipHeader + sdpBody;
    	}
    	else if(method == "200OKNOSDP")
    	{
        	sdpLength = 0;
        	sipHeader = setSipHeader("200OK", src_addr);
    		sipData = sipStartLine + sipHeader; 
    	}
    	return sipData;
    }
  
    public String setStartLine(String method)
    {
    	String reply = "";
    	
    	if(method == "INVITE")
    		reply = "SIP/2.0 180 Ringing" + CRLF;
    	else if(method == "ACK")
    		reply = "SIP/2.0 200 OK" + CRLF;
    	else if(method == "200OK")
    		reply = "SIP/2.0 200 OK" + CRLF;
    	else if(method == "BYE")
    		reply = "SIP/2.0 481 No Dialog found" + CRLF;
    	else
    		reply = "SIP/2.0 200 OK" + CRLF;
    	    	
    	return reply;    	
    }
    
    public String setSipHeader(String method, String src_addr) {
        String header = "Via: SIP/2.0/UDP " + src_addr+ ":5060;" + "branch=" + stringBranch + CRLF
                + "From: " + stringFrom + ";" + stringFromTag + CRLF
                + "To: " + stringTo + ";tag=123456789" + CRLF
                + "Call-ID: " + stringCallID + CRLF
                + "CSeq: " + stringCallSeq + CRLF
                + "Allow: INVITE, ACK, CANCEL, BYE" + CRLF
                + "Supported: replaces, timer" + CRLF
                + "Max-Forwards: 70" + CRLF
                + "Accept: application/sdp" + CRLF
                + "Contact: " + stringTo + ":5060" + CRLF
                + "Content-Type: application/sdp" + CRLF
                + "Content-Length: " + sdpLength + CRLF + CRLF;
        return header;
    }	
}

